首页 / 技术类 / C++ / 这种代码结构如何组织?goto or do…while(0)?

这种代码结构如何组织?goto or do…while(0)?

2010-03-30 09:55:00

灰常感谢各位达人昨天的热心回帖,让我受益匪浅。我仰望夜空,群星点点,就如各位的点睛之语,在无尽的苍穹闪耀。这让我深深地意识到,在这里,不仅可以分享成果,也可以分享困惑、分享寂寞。(开场白到此结束~)

在平常的编程中,我发现很容易遇到这种结构:

(1号方案)

 1BOOL foo()
 2{
 3    BOOL bRet = FALSE;
 4
 5    HANDLE hProcess = OpenProcess(...);
 6
 7    if (hProcess != NULL)
 8    {
 9        HANDLE hToken = OpenProcessToken(hProcess, ...);
10
11        if (hToken != NULL)
12        {
13            // ...
14
15            if (LookupPrivilegeValue(...))
16            {
17                if (AdjustTokenPrivileges(hToken, ...))
18                {
19                    bRet = TRUE;
20                }
21            }
22
23            CloseHandle(hToken);
24        }
25
26        CloseHandle(hProcess);
27    }
28
29    return bRet;
30}

如上写法,容易造成缩进级别不断增加。为了避免这种情况,可以改成:

(2号方案)

 1BOOL foo()
 2{
 3    HANDLE hProcess = OpenProcess(...);
 4
 5    if (hProcess == NULL)
 6    {
 7        return FALSE;
 8    }
 9
10    HANDLE hToken = OpenProcessToken(hProcess, ...);
11
12    if (hToken == NULL)
13    {
14        CloseHandle(hProcess);
15
16        return FALSE;
17    }
18
19    // ...
20
21    if (!LookupPrivilegeValue(...))
22    {
23        CloseHandle(hToken);
24        CloseHandle(hProcess);
25
26        return FALSE;
27    }
28
29    if (!AdjustTokenPrivileges(hToken, ...))
30    {
31        CloseHandle(hToken);
32        CloseHandle(hProcess);
33
34        return FALSE;
35    }
36
37    CloseHandle(hToken);
38    CloseHandle(hProcess);
39
40    return TRUE;
41}

这样,又引来了新的问题,每次 return FALSE 时的清理任务比较麻烦,要是每步操作都引进新的 HANDLE 的话,后续的清理工作就变得非常繁重。有人推荐do…while(0)的结构,有人推荐goto。这两种形式分别是——

do…while(0):

(3号方案)

 1BOOL foo()
 2{
 3    HANDLE hProcess = OpenProcess(...);
 4
 5    if (hProcess == NULL)
 6    {
 7        return FALSE;
 8    }
 9
10    BOOL bRet = FALSE;
11
12    do 
13    {
14        HANDLE hToken = OpenProcessToken(hProcess, ...);
15
16        if (hToken == NULL)
17        {
18            break;
19        }
20
21        // ...
22
23        BOOL bRetInner = FALSE;
24
25        do 
26        {
27            if (!LookupPrivilegeValue(...))
28            {
29                break;
30            }
31
32            if (!AdjustTokenPrivileges(hToken, ...))
33            {
34                break;
35            }
36
37            bRetInner = TRUE;
38
39        } while (0);
40
41        CloseHandle(hToken);
42
43        if (!bRetInner)
44        {
45            break;
46        }
47
48        bRet = TRUE;
49
50    } while (0);
51
52    CloseHandle(hProcess);
53
54    return bRet;
55}

这种结构可以避免每次 return FALSE 前的一堆清理工作,但缺点是,有几个依赖性的 HANDLE,就要嵌套几层的 do…while(0),有时候也会遇到需要三四层嵌套的情形。

goto:

(4.1号方案)

 1BOOL foo()
 2{
 3    BOOL bRet = FALSE;
 4
 5    HANDLE hProcess = OpenProcess(...);
 6
 7    if (hProcess == NULL)
 8    {
 9        goto CLEAR;
10    }
11
12    HANDLE hToken = OpenProcessToken(hProcess, ...);
13
14    if (hToken == NULL)
15    {
16        goto CLEAR;
17    }
18
19    // ...
20
21    if (!LookupPrivilegeValue(...))
22    {
23        goto CLEAR;
24    }
25
26    if (!AdjustTokenPrivileges(hToken, ...))
27    {
28        goto CLEAR;
29    }
30
31    bRet = TRUE;
32
33CLEAR:
34    if (hToken != NULL)
35    {
36        CloseHandle(hToken);
37    }
38
39    if (hProcess != NULL)
40    {
41        CloseHandle(hProcess);
42    }
43
44    return bRet;
45}

(4.2号方案)

 1BOOL foo()
 2{
 3    BOOL bRet = FALSE;
 4
 5    HANDLE hProcess = OpenProcess(...);
 6
 7    if (hProcess == NULL)
 8    {
 9        goto ERROR_LEVEL0;
10    }
11
12    HANDLE hToken = OpenProcessToken(hProcess, ...);
13
14    if (hToken == NULL)
15    {
16        goto ERROR_LEVEL1;
17    }
18
19    // ...
20
21    if (!LookupPrivilegeValue(...))
22    {
23        goto ERROR_LEVEL2;
24    }
25
26    if (!AdjustTokenPrivileges(hToken, ...))
27    {
28        goto ERROR_LEVEL2;
29    }
30
31    bRet = TRUE;
32
33ERROR_LEVEL2:
34    CloseHandle(hToken);
35ERROR_LEVEL1:
36    CloseHandle(hProcess);
37ERROR_LEVEL0:
38    return bRet;
39}

(4.1和4.2哪种好一点。。。?)

在这种情形下,goto 的方案似乎是完美的。但是 goto 如果遇到 C++,缺点体现出来了。下面这一段,现在是 do…while(0) 结构(只有一层嵌套,这种结构用在这里还算合理):

 1BOOL foo()
 2{
 3    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); 
 4
 5    while (true)
 6    {
 7        if (FAILED(hr))
 8        {
 9            break;
10        }
11
12        hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
13
14        if (FAILED(hr))
15        {
16            break;
17        }
18
19        CComPtr<IWbemLocator> pLoc = NULL;
20        hr = pLoc.CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER);
21
22        if (FAILED(hr))
23        {
24            break;
25        }
26
27        CComPtr<IWbemServices> pSvc = NULL;
28        hr = pLoc->ConnectServer(_T("ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &pSvc);
29
30        if (FAILED(hr))
31        {
32            break;
33        }
34
35        hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
36
37        if (FAILED(hr))
38        {
39            break;
40        }
41
42        CComPtr<IEnumWbemClassObject> pEnum = NULL;
43        _bstr_t bstrLang = _T("WQL");
44        _bstr_t bstrSql = _T("SELECT * FROM __InstanceCreationEvent WITHIN 10")
45            _T("WHERE TargetInstance ISA 'Win32_LogonSession' AND (TargetInstance.LogonType = 2 OR TargetInstance.LogonType = 11)");
46        hr = pSvc->ExecNotificationQuery(bstrLang, bstrSql, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum);
47
48        if (FAILED(hr))
49        {
50            break;
51        }
52
53        ULONG uCount = 1;
54        CComPtr<IWbemClassObject> pNext = NULL;
55        hr = pEnum->Next(WBEM_INFINITE, uCount, &pNext, &uCount);
56
57        if (FAILED(hr))
58        {
59            break;
60        }
61
62        // ...
63
64        break;
65    }
66
67    CoUninitialize();
68
69    return SUCCEEDED(hr);
70}

如果改成 goto,则需要把所有需要对象的定义全放到最前面来,不然 goto 会跳过他们的初始化,编译不过。但是,所有对象都放到最前面定义,又违反了即用即声明的规则,而且太多了也容易混淆。

最后,问题是,如果遇到 C++ 的、多层嵌套的,大家一般如何组织代码呢?

谢谢!


首发:http://www.cppblog.com/Streamlet/archive/2010/03/30/110955.html



NoteIsSite/0.4